#include "stdafx.h"
#include "textPrinter.h"

CTextPrinter::CTextPrinter()
	: m_indentLevel( 0 )
{
	Clear();
}

void CTextPrinter::Clear()
{
	m_indentLevel = 0;
	m_buf.clear();
}

void CTextPrinter::Indent()
{
	m_indentLevel += 1;
}

void CTextPrinter::Unindent()
{
	if ( m_indentLevel > 0 )
		m_indentLevel -= 1;
}

static void AppendChar( std::string& txt, const char ch )
{
	const char shortStr[] = { ch, 0 };
	txt += shortStr;
}

void CTextPrinter::Write( const char* txt )
{
	const char* pos = txt;
	while ( *pos )
	{
		const char ch = *pos++;

		// eat this shit
		if ( ch == '\r' )
			continue;

		// new line
		if ( ch == '\n' )
		{
			// full line break
#ifdef WIN32
			AppendChar( m_buf, '\r' );
#endif
			AppendChar( m_buf, '\n' );

			// indent
			for ( int i=0; i<m_indentLevel; ++i )
				AppendChar( m_buf, '\t' );

			continue;
		}

		// normal stuff
		AppendChar( m_buf, ch );
	}
}

void CTextPrinter::Writef( const char* txt, ... )
{
	char buf[ 8192 ];
	va_list args;

	va_start( args, txt );
#ifdef __linux__
	assert( txt != nullptr);
	vsprintf( buf, txt, args );
#else
	vsprintf_s( buf, sizeof(buf), txt, args );
#endif
	va_end( args );

	Write( buf );
}

#pragma optimize("",off)

bool CTextPrinter::SaveToFile( const std::string& outputFilePath, bool& wasUpToDate ) const
{
	// create the path
	{
		const char* start = outputFilePath.c_str();
		const char* cur = start;
		while ( *cur )
		{
			if ( *cur == '/' || *cur == '\\' )
			{
				const int length = ( cur - start );
				if ( length > 4 )
				{
					const std::string dirPath( start, length );
#ifdef WIN32
					CreateDirectoryA( dirPath.c_str(), NULL );
#else
					// detect windows-only paths
					assert( *cur != '\\' );

					// check if it already exists
					DIR* dir = opendir( dirPath.c_str() );
					if ( !dir )
					{
						// create it if not
						size_t pos = 0;
						while ( pos != std::string::npos )
						{
							pos = dirPath.find( '/', pos + 1 );

							const std::string nextDir = dirPath.substr( 0, pos );
							mkdir( nextDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH );
						}
					}
#endif			
				}
			}
			cur += 1;
		}
	}

	// load current content
	{
		bool alreadyThere = false;

		// get access to file
		std::ifstream hFile( outputFilePath.c_str(), std::ios::in | std::ios::binary );
		if ( hFile )
		{
			// get file size
			hFile.seekg( 0, std::ios::end );
			size_t fileSize = hFile.tellg();
			hFile.seekg( 0, std::ios::beg );

			// allocate string
			std::string ret;
			ret.resize( fileSize, ' ' );

			// load data
			hFile.read( &ret[0], fileSize );

			size_t written = hFile.tellg();
			if ( written == fileSize && ret == m_buf )
			{
				// data is already there, DO NOT UPDATE TEH FILE
				alreadyThere = true;
			}

			// Close file
			hFile.close();
		}

		// we already have the data
		if ( alreadyThere )
		{
			wasUpToDate = true;
			return true;
		}
	}

	// get access to file
	std::ofstream hFile;
	// open in binary mode, otherwise newlines break
	hFile.open( outputFilePath.c_str(), std::ios::out | std::ios::trunc | std::ios::binary );
	if (! hFile )
	{
		fprintf( stderr, "Unable to write to '%s'\n", outputFilePath.c_str() );
		return false;
	}

	// store file data
	hFile.write(m_buf.c_str(), m_buf.length());
	hFile.flush();
	size_t written = hFile.tellp();

	// close file
	hFile.close();

	// return success code only if whole file was written
	wasUpToDate = false;
	if ( written != m_buf.length() )
	{
		fprintf( stderr, "Error writing to '%s'\n", outputFilePath.c_str() );
		return false;
	}

	// saved
	return true;
}
